﻿/*
 *  date: 2018-07-05
 *  author: John-chen
 *  cn: UI管理
 *  en: todo:
 */

using System;
using System.Linq;
using UnityEngine;
using System.Collections.Generic;
using System.Collections;

namespace JyFramework
{
    /// <summary>
    /// UI管理
    /// </summary>
    public class UIController : SingletonMgr<UIController>, IUIFunction
    {
        /// <summary>
        /// UI 跟节点
        /// </summary>
        public Transform UIRoot { get { return _uiRoot?.transform;} }

        /// <summary>
        /// UI相机
        /// </summary>
        public Camera UICamera
        {
            get { return UIConfig.UIFKType == UIFKType.UGUI ? _uguiRootInfo.UICamera : _nguiRootInfo.UICamera; }
        }

        /// <summary>
        /// 获取窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetWindow<T>() where T : BaseWindow
        {
            var myType = typeof (T);
            T tWin = null;

            BaseWindow win;
            if (_showWindows.TryGetValue(myType, out win))
            {
                tWin = (T) win;
            }
            else if (_hideWindows.TryGetValue(myType, out win))
            {
                tWin = (T) win;
            }

            return tWin;
        }

        /// <summary>
        /// 显示一个窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        /// <param name="args"></param>
        public T Show<T>(WndAction action = null) where T : BaseWindow, new()
        {
            var myType = typeof(T);
            BaseWindow window = null;

            if (_hideWindows.TryGetValue(myType, out window))
            {
                if (!window.CheckCacheTime())
                {
                    AddToShowWindows<T>(myType, window, action);
                    return (T)window;
                }

                window.Destroy();
                RemoveFromWindowContainer(ref _hideWindows, myType);
            }

            if (_showWindows.TryGetValue(myType, out window))
            {
                ShowWindow(window, action);
                return (T)window;
            }

            return LoadWindow<T>(action);
        }

        /// <summary>
        /// 关闭一个窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        /// <param name="args"></param>
        public T Hide<T>(WndAction action = null) where T : BaseWindow
        {
            var myType = typeof (T);
            BaseWindow window = null;

            if (_showWindows.TryGetValue(myType, out window))
            {
                AddToHideWindows(myType, window);
                return (T)window;
            }

            if (_hideWindows.TryGetValue(myType, out window))
            {
                HideWindow(window);
                return (T)window;
            }

            return null;
        }

        /// <summary>
        /// 影藏所有的窗口
        /// </summary>
        public void HideAll()
        {
            var showKeys = _showWindows.Keys.ToArray();
            int cnt = showKeys.Length - 1;

            for (int i = cnt; i >= 0; i--)
            {
                AddToHideWindows(showKeys[i], _showWindows[showKeys[i]]);
            }
        }

        /// <summary>
        /// 销毁所有的窗口
        /// </summary>
        public void ClearAll()
        {
            HideAll();
            var hideKeys = _hideWindows.Keys.ToArray();
            int cnt = hideKeys.Length - 1;

            for (int i = cnt; i >= 0; i--)
            {
                var key = hideKeys[i];
                var wnd = _hideWindows[key];
                wnd.Destroy();
            }

            _hideWindows.Clear();
        }

        /// <summary>
        /// 每帧更新所有打开的窗口
        /// </summary>
        /// <param name="deltaTime"></param>
        public void Update(float deltaTime)
        {
            foreach (var window in _showWindows.Values)
            {
                window.OnUpdate(deltaTime);
            }
        }

        /// <summary>
        /// 创建UIRoot
        /// </summary>
        public void CreateUIRoot()
        {
            var root = GameObject.Instantiate(_resLoader.LoadRes<GameObject>(PathHelper.UIRootPath));
            SetUIRoot(root);
        }

        /// <summary>
        /// 添加到窗口容器中
        /// </summary>
        /// <param name="container"></param>
        /// <param name="type"></param>
        /// <param name="window"></param>
        private void AddToWindowContainer(ref Dictionary<Type, BaseWindow> container, Type type, BaseWindow window)
        {
            if (container.ContainsKey(type))
            {
                container[type] = window;
            }
            else
            {
                container.Add(type, window);
            }
        }

        /// <summary>
        /// 从窗口容器中移除
        /// </summary>
        /// <param name="container"></param>
        /// <param name="type"></param>
        /// <param name="window"></param>
        private void RemoveFromWindowContainer(ref Dictionary<Type, BaseWindow> container, Type type)
        {
            if (container.ContainsKey(type))
            {
                container.Remove(type);
            }
        }

        /// <summary>
        /// 添加隐藏列表
        /// </summary>
        /// <param name="type"></param>
        /// <param name="window"></param>

        private void AddToHideWindows(Type type, BaseWindow window, WndAction action = null)
        {
            RemoveFromWindowContainer(ref _showWindows, type);
            AddToWindowContainer(ref _hideWindows, type, window);

            HideWindow(window, action);

            CheckCacheTime(window, type);
        }

        /// <summary>
        /// 添加到显示列表
        /// </summary>
        /// <param name="type"></param>
        /// <param name="window"></param>
        private void AddToShowWindows<T>(Type type, BaseWindow window, WndAction action = null) where T : BaseWindow
        {
            RemoveFromWindowContainer(ref _hideWindows, type);
            AddToWindowContainer(ref _showWindows, type, window);

            // 设置父节点、层级等
            SetWindow<T>((T)window);

            // 显示窗口
            ShowWindow(window, action);
        }

        /// <summary>
        /// 影藏窗口
        /// </summary>
        /// <param name="window"></param>
        private void HideWindow(BaseWindow window, WndAction action = null)
        {
            if (window == null) return;

            window.gameObject.SetActive(false);
            window.OnHide();
            window.WndInfo.WinState = WindowState.InHide;

            action?.Execute();
        }

        /// <summary>
        /// 显示窗口
        /// </summary>
        /// <param name="window"></param>
        private void ShowWindow(BaseWindow window, WndAction action = null)
        {
            if (window == null) return;

            window.RefreshWndCache();
            window.OnBeforeShow();
            window.gameObject.SetActive(true);
            window.OnShow();
            window.WndInfo.WinState = WindowState.InShow;

            action?.Execute();
        }

        /// <summary>
        /// 检测是否达到缓存时间
        /// </summary>
        /// <param name="window"></param>
        private void CheckCacheTime(BaseWindow window, Type type)
        {
            if (window.CheckCacheTime())
            {
                window.Destroy();
                RemoveFromWindowContainer(ref _hideWindows, type);
            }
        }


        /// <summary>
        /// 同步创建UI实例
        /// </summary>
        /// <typeparam name="T"> UI窗口类 </typeparam>
        /// <param name="winType"> UI窗口类型 </param>
        /// <param name="action"> 创建成功后的回调 </param>
        /// <returns></returns>
        private T LoadWindow<T>(WndAction action) where T : BaseWindow, new()
        {
            Type winType = typeof (T);
            // 创建一个窗口
            T wnd = new T();
            wnd?.SetWndInfo(UIConfig.GetPath<T>(), action);
            wnd.WndInfo.WinState = WindowState.Created;

            // 添加到显示列表
            AddToShowWindows<T>(winType, wnd);

            return wnd;
        }

        /// <summary>
        /// 设置窗口的信息
        /// 父节点、层级等
        /// </summary>
        /// <param name="type"></param>
        /// <param name="window"></param>
        private void SetWindow<T>(T window) where T : BaseWindow
        {
            switch (UIConfig.UIFKType)
            {
                case UIFKType.UGUI:
                    _uguiRootInfo.SetWindow<T>(window);
                    break;
                case UIFKType.NGUI:
                    _nguiRootInfo.SetWindow<T>(window);
                    break;
            }
        }

        /// <summary>
        /// 设置UIRoot
        /// </summary>
        /// <param name="rootObj"></param>
        private void SetUIRoot(GameObject rootObj)
        {
            if (rootObj == null)
            {
                Debug.LogError("The ui root obj load failed!");
                return;
            }

            _uiRoot = rootObj;

            var gameAppObj = _gameApp.transform;
            _uiRoot.name = "UIRoot";
            _uiRoot.transform.SetParent(gameAppObj);
            _uiRoot.transform.localPosition = Vector3.zero;
            _uiRoot.transform.localScale = Vector3.one;
            _uiRoot.transform.localEulerAngles = Vector3.zero;

            switch (UIConfig.UIFKType)
            {
                case UIFKType.NGUI:
                    _nguiRootInfo = new NGUIRootInfo(rootObj, SetNGUIWindow);
                    break;
                case UIFKType.UGUI:
                    _uguiRootInfo = new UGUIRootInfo(rootObj, SetUGUIWindow);
                    break;
            }
        }

        /// <summary>
        /// 设置NGUI的信息
        /// </summary>
        /// <param name="window"></param>
        private void SetNGUIWindow(BaseWindow window)
        {

        }

        /// <summary>
        /// 设置UGUI的信息
        /// </summary>
        /// <param name="window"></param>
        private void SetUGUIWindow(BaseWindow window)
        {
            window.gameObject.transform.SetParent(_uguiRootInfo.RootCanvas.transform, false);
            //window.gameObject.transform.localPosition = Vector3.zero;
            //window.gameObject.transform.localScale = Vector3.one;
            //window.gameObject.transform.localEulerAngles = Vector3.zero;
            //window.gameObject.transform.localRotation = Quaternion.AngleAxis(0, Vector3.up);
            window.gameObject.name = window.Name;
            window.gameObject.transform.SetAsLastSibling();
        }


        // todo:
        private IEnumerator TestSet(BaseWindow window)
        {
            MonoHelper.Ins.StartCoroutine(TestSet(window));
            yield return null;
            yield break;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        protected override void Init()
        {
            _showWindows = new Dictionary<Type, BaseWindow>();
            _hideWindows = new Dictionary<Type, BaseWindow>();

            _app = JyApp.Ins;
            _gameApp = JyApp.GameApp;
            _resLoader = _app.GetModule<ResourceModule>(ModuleName.Res).Loader;
        }

        protected JyApp _app;
        protected GameObject _gameApp;
        protected ResLoader _resLoader;

        protected GameObject _uiRoot;
        protected NGUIRootInfo _nguiRootInfo;
        protected UGUIRootInfo _uguiRootInfo;

        protected Dictionary<Type, BaseWindow> _showWindows;
        protected Dictionary<Type, BaseWindow> _hideWindows;
    }
}